﻿using System.Collections.Generic;
using SpiceNet.Circuits;
using SpiceNet.Diagnostics;
using System.Numerics;
using SpiceNet.Parameters;
using SpiceNet.Simulations;

namespace SpiceNet.Models
{
    /// <summary>
    /// A class describing a capacitor
    /// </summary>
    public class Capacitor : Instance
    {
        /// <summary>
        /// Private variables
        /// </summary>
        private int CAPstate, CAPposNode, CAPnegNode;

        /// <summary>
        /// Get parameters
        /// </summary>
        public override Dictionary<string, IP> ParamTable => _pTable;

        #region Parameters
        private enum _c
        {
            CAP_CAP = 1,
            CAP_IC = 2,
            CAP_WIDTH = 3,
            CAP_LENGTH = 4,
            CAP_CAP_SENS = 5,
            CAP_CURRENT = 6,
            CAP_POWER = 7,
            CAP_QUEST_SENS_REAL = 201,
            CAP_QUEST_SENS_IMAG = 202,
            CAP_QUEST_SENS_MAG = 203,
            CAP_QUEST_SENS_PH = 204,
            CAP_QUEST_SENS_CPLX = 205,
            CAP_QUEST_SENS_DC = 206
        }
        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "capacitance", new IP(IF.IOPAP, (int)_c.CAP_CAP, T.REAL, "Device capacitance") },
            { "ic", new IP(IF.IOPAU, (int)_c.CAP_IC, T.REAL, "Initial capacitor voltage") },
            { "w", new IP(IF.IOPAU, (int)_c.CAP_WIDTH, T.REAL, "Device width") },
            { "l", new IP(IF.IOPAU, (int)_c.CAP_LENGTH, T.REAL, "Device length") },
            { "sens_cap", new IP(IF.IP, (int)_c.CAP_CAP_SENS, T.FLAG, "flag to request sens. WRT cap.") },
            { "i", new IP(IF.OP, (int)_c.CAP_CURRENT, T.REAL, "Device current") },
            { "p", new IP(IF.OP, (int)_c.CAP_POWER, T.REAL, "Instantaneous device power") },
            { "sens_dc", new IP(IF.OPU, (int)_c.CAP_QUEST_SENS_DC, T.REAL, "dc sensitivity ") },
            { "sens_real", new IP(IF.OPU, (int)_c.CAP_QUEST_SENS_REAL, T.REAL, "real part of ac sensitivity") },
            { "sens_imag", new IP(IF.OPU, (int)_c.CAP_QUEST_SENS_IMAG, T.REAL, "dc sens. & imag part of ac sens.") },
            { "sens_mag", new IP(IF.OPU, (int)_c.CAP_QUEST_SENS_MAG, T.REAL, "sensitivity of ac magnitude") },
            { "sens_ph", new IP(IF.OPU, (int)_c.CAP_QUEST_SENS_PH, T.REAL, "sensitivity of ac phase") },
            { "sens_cplx", new IP(IF.OPU, (int)_c.CAP_QUEST_SENS_CPLX, T.COMPLEX, "ac sensitivity") }
        };

        /// <summary>
        /// Capacitance
        /// </summary>
        public Parameter<double> CAPcapac { get; } = new Parameter<double>();
        public Parameter<double> CAPinitCond { get; } = new Parameter<double>();
        public Parameter<double> CAPwidth { get; } = new Parameter<double>();
        public Parameter<double> CAPlength { get; } = new Parameter<double>();
        #endregion

        #region constants
        private const int CAPqcap = 0;
        private const int CAPccap = 1;
        private const int CAPsensxp = 2;
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">The name of the capacitor</param>
        public Capacitor(string name) : base(name, 2, typeof(CapacitorModel)) { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">The name of the capacitor</param>
        /// <param name="posnode">The name of node 1</param>
        /// <param name="negnode">The name of node 2</param>
        /// <param name="capacitance">The capacitance (F)</param>
        public Capacitor(string name, string posnode, string negnode, double capacitance) : this(name)
        {
            Connect(posnode, negnode);
            CAPcapac.Par(capacitance);
        }

        /// <summary>
        /// Set parameters
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.CAP_CAP:
                    CAPcapac.Par((double)value);
                    break;
                case _c.CAP_IC:
                    CAPinitCond.Par((double)value);
                    break;
                case _c.CAP_WIDTH:
                    CAPwidth.Par((double)value);
                    break;
                case _c.CAP_LENGTH:
                    CAPlength.Par((double)value);
                    break;
                case _c.CAP_CAP_SENS:
                    // CAPsenParmNo = (int)value;
                    break;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Ask parameters
        /// </summary>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.CAP_CAP:
                    return CAPcapac.Value;
                case _c.CAP_IC:
                    return CAPinitCond.Value;
                case _c.CAP_WIDTH:
                    return CAPwidth.Value;
                case _c.CAP_LENGTH:
                    return CAPlength.Value;
                case _c.CAP_CURRENT:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                    {
                        throw new AcCurrentPowerException();
                    }
                    else if ((ckt.CurrentAnalysis & (AnalysisType.DoingDcOp | AnalysisType.DoingTrCv)) != 0)
                    {
                        return 0;
                    }
                    else if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingTran))
                    {
                        if (ckt.Mode.HasFlag(Circuit.Modes.TranOp))
                        {
                            return 0;
                        }
                        else
                        {
                            return ckt.State.States[0][CAPstate + CAPccap];
                        }
                    }
                    break;
                case _c.CAP_POWER:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                    {
                        throw new AcCurrentPowerException();
                    }
                    else if ((ckt.CurrentAnalysis & (AnalysisType.DoingDcOp | AnalysisType.DoingTrCv)) != 0)
                    {
                        return 0;
                    }
                    else if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingTran))
                    {
                        if (ckt.Mode.HasFlag(Circuit.Modes.TranOp))
                        {
                            return 0;
                        }
                        else
                        {
                            return ckt.State.States[0][CAPstate + CAPccap] *
                                    (ckt.State.Solution[CAPposNode] -
                                    ckt.State.Solution[CAPnegNode]);
                        }
                    }
                    break;
                default:
                    throw new BadParameterException();
            }
            return null;
        }

        /// <summary>
        /// Setup the model
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void Setup(Circuit ckt, Model model, object p = null)
        {
            // Store the indices
            CircuitNode[] nodes = BindNodes(ckt);
            CAPposNode = nodes[0].Number;
            CAPnegNode = nodes[1].Number;

            // Create two more states in order to do the integration
            CAPstate = CreateState(ckt, 2);

            // When sensitivity is also handled, add more lines here
        }

        /// <summary>
        /// Do temperature-dependent setup
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void Temperature(Circuit ckt, Model mod, object p = null)
        {
            CapacitorModel model = mod as CapacitorModel;

            /* Default Value Processing for Capacitor Instance */
            if (!CAPwidth.Given)
            {
                CAPwidth.Value = model.CAPdefWidth;
            }
            if (!CAPcapac.Given)
            {
                CAPcapac.Value =
                        model.CAPcj *
                            (CAPwidth - model.CAPnarrow) *
                            (CAPlength - model.CAPnarrow) +
                        model.CAPcjsw * 2 * (
                            (CAPlength - model.CAPnarrow) +
                            (CAPwidth - model.CAPnarrow));
            }
        }

        /// <summary>
        /// Load the Yn-matrix and vector with the capacitor info
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void Load(Circuit ckt, Model model, object p = null)
        {
            double vcap;
            bool cond1 = (bool)p;
            CircuitState state = ckt.State;

            if (cond1)
            {
                vcap = CAPinitCond;
            }
            else
            {
                vcap = state.OldSolution[CAPposNode] - state.OldSolution[CAPnegNode];
            }
            if ((ckt.Mode & (Circuit.Modes.Tran | Circuit.Modes.Ac)) != 0)
            {
                state.States[0][CAPstate + CAPqcap] = CAPcapac * vcap;
                if (ckt.Mode.HasFlag(Circuit.Modes.InitTran))
                {
                    state.States[1][CAPstate + CAPqcap] = state.States[0][CAPstate + CAPqcap];
                }
                var result = ckt.Integration.Integrate(state, CAPstate + CAPqcap, CAPcapac);
                if (ckt.Mode.HasFlag(Circuit.Modes.InitTran))
                    state.States[1][CAPstate + CAPccap] = state.States[0][CAPstate + CAPccap];

                state.Matrix[CAPposNode, CAPposNode] += result.Geq;
                state.Matrix[CAPnegNode, CAPnegNode] += result.Geq;
                state.Matrix[CAPposNode, CAPnegNode] -= result.Geq;
                state.Matrix[CAPnegNode, CAPposNode] -= result.Geq;
                state.Rhs[CAPposNode] -= result.Ceq;
                state.Rhs[CAPnegNode] += result.Ceq;
            }
            else
                state.States[0][CAPstate + CAPqcap] = CAPcapac * vcap;
        }

        /// <summary>
        /// Load the capacitor in the circuit for AC analysis
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void AcLoad(Circuit ckt, Model model, object p = null)
        {
            CircuitStateComplex state = ckt.StateComplex;

            // Calculate the complex admittance
            Complex val = state.Omega * CAPcapac.Value;

            // Capacitance matrix
            state.Matrix[CAPposNode, CAPposNode] += val;
            state.Matrix[CAPposNode, CAPnegNode] -= val;
            state.Matrix[CAPnegNode, CAPposNode] -= val;
            state.Matrix[CAPnegNode, CAPnegNode] += val;
        }
    }
}
